package com.castlzl.random.core.tinyid;

import com.castlzl.random.core.IdGenerator;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;

import java.util.concurrent.atomic.AtomicLong;

/**
 * 基于滴滴Tinyid的实现
 * 使用号段模式生成ID，支持双buffer和异步加载优化
 */
@Slf4j
public class TinyIdGenerator implements IdGenerator {

    /**
     * 号段对象，包含当前号段的起始值和最大值
     */
    @Getter
    private volatile Segment currentSegment;

    /**
     * 下一个号段，用于异步加载
     */
    private volatile Segment nextSegment;

    /**
     * 当前ID值
     */
    private final AtomicLong currentId;

    /**
     * 业务标签
     */
    private final String bizTag;

    /**
     * 号段服务，用于获取新的号段
     */
    private final TinyIdSegmentService tinyIdSegmentService;

    /**
     * 是否异步加载下一个号段
     */
    private final boolean asyncLoadingSegment;

    /**
     * 更新号段的阈值，当前号段使用量达到阈值时触发更新下一个号段
     */
    private static final int UPDATE_PERCENT = 70;

    public TinyIdGenerator(String bizTag, TinyIdSegmentService tinyIdSegmentService, boolean asyncLoadingSegment) {
        this.bizTag = bizTag;
        this.tinyIdSegmentService = tinyIdSegmentService;
        this.asyncLoadingSegment = asyncLoadingSegment;
        this.currentId = new AtomicLong(0);
        loadNextSegment();
        this.currentSegment = nextSegment;
        this.nextSegment = null;
        this.currentId.set(currentSegment.getMin() - 1); // 初始化为min-1，因为第一次incrementAndGet会加1
        log.warn("获取到tiny currentId:{},nextId:{}", currentId.get(), currentId.get() + 1);
    }

    @Override
    public synchronized long nextId() {
        if (currentId.get() > currentSegment.getMax()) {
            waitAndSwitchSegment();
        }

        long id = currentId.incrementAndGet();

        if (asyncLoadingSegment && needLoadNextSegment()) {
            loadNextSegmentAsync();
        }

        return id;
    }

    private boolean needLoadNextSegment() {
        return nextSegment == null && currentId.get() > currentSegment.getThreshold();
    }

    private void loadNextSegmentAsync() {
        new Thread(this::loadNextSegment).start();
    }

    private synchronized void loadNextSegment() {
        try {
            this.nextSegment = tinyIdSegmentService.getNextSegment(bizTag);
        } catch (Exception e) {
            log.error("Failed to load next segment for bizTag: {}", bizTag, e);
        }
    }

    private void waitAndSwitchSegment() {
        while (nextSegment == null) {
            loadNextSegment();
            if (nextSegment == null) {
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    log.error("Interrupted while waiting for next segment", e);
                    Thread.currentThread().interrupt();
                }
            }
        }

        currentSegment = nextSegment;
        nextSegment = null;
        currentId.set(currentSegment.getMin());
    }

    @Override
    public String getName() {
        return "tinyid";
    }

    @Getter
    public static class Segment {
        private final long min;
        private final long max;
        private final long threshold;

        public Segment(long min, long max) {
            this.min = min;
            this.max = max;
            this.threshold = min + (max - min) * UPDATE_PERCENT / 100;
        }
    }
}